
#ifndef	__N_MAPER_HPP_2010__
#define	__N_MAPER_HPP_2010__


/**********************************************************************
 *

Examples :

#include "boque/nmaper.hpp"


// map with any data via md5sum, invoke class Md5sumMaper
// map id locates [ 1 .. 256 ]
{
	// Md5sumMaper( int max=65536, int min=1, int bits=0 )
	NMaper*	map_p = new Md5sumMaper( 256, 1 );

	const char*	data = "anything to hash";
	int	id = map_p->map( data, strlen(data) );
	// you got an id whose value is bwteen [ 1 .. 256 ]

	delete map_p;
}
{
	// same as last sample, except mapping in a larger space (bits=16)

	// Md5sumMaper( int max=65536, int min=1, int bits=0 )
	NMaper*	map_p = new Md5sumMaper( 256, 1, 16 );

	const char*	data = "anything to hash";
	int	id = map_p->map( data, strlen(data) );
	// you got an id whose value is bwteen [ 1 .. 256 ]

	delete map_p;
}


// map from md5sum in binary format, invoke class Md5binMaper
// map id locates [ 1 .. 4096 ]
{
	// Md5binMaper( int max=65536, int min=1, int bits=0 )
	NMaper*	map_p = new Md5binMaper( 4096, 1 );

	const char*	data = md5sum( "anything to hash" );
	int	id = map_p->map( data, 16 );
	// you got an id whose value is bwteen [ 1 .. 4096 ]

	delete map_p;
}


// map from md5sum in hexical format, invoke class Md5hexMaper
// map id locates [ 1 .. 8192 ]
{
	// Md5hexMaper( int max=65536, int min=1, int bits=0 )
	NMaper*	map_p = new Md5hexMaper( 8192, 1 );

	const char*	data = md5hex( "anything to hash" );
	int	id = map_p->map( data, 32 );
	// you got an id whose value is bwteen [ 1 .. 8192 ]

	delete map_p;
}


 **********************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>

#include <stdexcept>

#include "bocat/common/md5.hpp"


class	NMaper
{
public	:
	explicit 
	NMaper(int max=65536, int min=1)
		:	_max(max), _min(min)
	{
		if ( _max < _min ) {
			int	tmp = _max;
			_max = _min;
			_min = tmp;
		}
	}

	virtual ~NMaper()
	{
	}

public	:
	/*********************************************************************
	 * calculate hash value for data
	 * 
	 * @param data        buffer of data to calculate
	 * @param data_size   size of data in buffer
	 *
	 * @return hash value of data ( in [ min .. max ] )
	 *
	 *********************************************************************/
	virtual	int	map( const void* data, size_t data_size ) = 0;

public	:
	int	max( void ) { return _max; }
	const int max( void ) const { return _max; }

	int min( void ) { return _min; }
	const int min( void ) const { return _min; }

	int	size( void ) { return (_max - _min + 1); }
	const int size( void ) const { return (_max - _min + 1); }

protected	:
	unsigned	toint( const void* md5_bin, int val_size )
	{
		const unsigned char* p = (const unsigned char* )md5_bin;

		unsigned	val = 0;
		if ( val_size <= 16 ) {
			val = p[0] >> 4;
		}
		else if ( val_size <= 256 ) {
			val = p[0];
		}
		else if ( val_size <= 4096 ) {
			val = (p[0] << 4) + (p[1] >> 4);
		}
		else if ( val_size <= 65536 ) {
			val = (p[0] << 8) + p[1];
		}
		else if ( val_size <= 1048576 ) {
			val = (p[0] << 12) + (p[1] << 4) + (p[2] >> 4);
		}
		else if ( val_size <= 16777216 ) {
			val = (p[0] << 16) + (p[1] << 8) + p[2];
		}
		else {
			val = (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; 
		}

		return val;
	}

	int		calcMapSize( int bits, int val_size )
	{
		int	map_size = val_size;

		if ( (bits < 0) || (bits > 32) ) {
			throw std::runtime_error("ASSERT ( 0 <= bits <= 32 )");
		}
		if ( bits > 0 ) {
			if ( bits < 31 ) {
				map_size = 1 << bits;
			}
			else {
				map_size = 0x7FFFFFFF;
			}
			if ( map_size < val_size ) {
				throw std::runtime_error("more bits are need.");
			}
		}

		return	map_size;
	}

public	:
	/**
	 * Decode a string encoded with hexadecimal encoding.
	 */
	static char* hexdecode(const char* str, char* outbuf, size_t* sp) 
	{
		if ( NULL == str ) { 
			return  NULL;
		}   

		size_t zsiz = strlen(str);
		char* zbuf = outbuf;
		char* wp = zbuf;
		for (size_t i = 0; i < zsiz; i += 2) { 
			while (str[i] >= '\0' && str[i] <= ' ') {
				i++; 
			}    
			int32_t num = 0;  
			int32_t c = str[i];
			if (c == '\0') break;
			if (c >= '0' && c <= '9') {
				num = c - '0'; 
			} else if (c >= 'a' && c <= 'f') {
				num = c - 'a' + 10; 
			} else if (c >= 'A' && c <= 'F') {
				num = c - 'A' + 10; 
			} else if (c == '\0') {
				break;
			}    
			c = str[i+1];
			if (c >= '0' && c <= '9') {
				num = num * 0x10 + c - '0'; 
			} else if (c >= 'a' && c <= 'f') {
				num = num * 0x10 + c - 'a' + 10; 
			} else if (c >= 'A' && c <= 'F') {
				num = num * 0x10 + c - 'A' + 10; 
			} else if (c == '\0') {
				break;
			}    
			*(wp++) = num; 
		}
		*wp = '\0';
		*sp = wp - zbuf;
		return zbuf;
	}


private	:
	int	_max, _min;

};	//// class NMaper


class	Md5sumMaper
		:	public	NMaper
{
public	:
	explicit 
	Md5sumMaper(int max=65536, int min=1, int bits=0)
		:	NMaper( max, min )
	{
		_val_size = size();

		_map_size = calcMapSize( bits, _val_size );
	}

	virtual ~Md5sumMaper()
	{
	}

public	:
	/*********************************************************************
	 * calculate hash value for data
	 * 
	 * @param data        buffer of data to calculate
	 * @param data_size   size of data in buffer
	 *
	 * @return hash value of data ( in [ min .. max ] )
	 *
	 *********************************************************************/
	virtual	int	map( const void* data, size_t data_size )
	{
		bocat::common::MD5  m;
		m.update( data, data_size );

		unsigned val = toint( m.digest(), _map_size );

		int	x = (val % _val_size) + min();
		return	x;
	}

private	:
	int	_val_size;
	int	_map_size;
};	//// class Md5sumMapper


class	Md5binMaper
		:	public	NMaper
{
public	:
	explicit 
	Md5binMaper(int max=65536, int min=1, int bits=0)
		:	NMaper( max, min )
	{
		_val_size = size();

		_map_size = calcMapSize( bits, _val_size );
	}

	virtual ~Md5binMaper()
	{
	}

public	:
	/*********************************************************************
	 * calculate hash value for data
	 * 
	 * @param data        buffer of MD5Sum ( 16-bytes ) 
	 * @param data_size   size of data in buffer ( =16 )
	 *
	 * @return hash value of data ( in [ min .. max ] )
	 *
	 *********************************************************************/
	virtual	int	map( const void* data, size_t data_size )
	{
		if ( 16 != data_size ) {
			return	-1;
		}

		unsigned val = toint( data, _map_size );

		int	x = (val % _val_size) + min();
		return	x;
	}

private	:
	int	_val_size;
	int	_map_size;
};	//// class Md5binMapper


class	Md5hexMaper
		:	public	NMaper
{
public	:
	explicit 
	Md5hexMaper(int max=65536, int min=1, int bits=0)
		:	NMaper( max, min )
	{
		_val_size = size();

		_map_size = calcMapSize( bits, _val_size );
	}

	virtual ~Md5hexMaper()
	{
	}

public	:
	/*********************************************************************
	 * calculate hash value for data
	 * 
	 * @param data        buffer of MD5Sum in hexical-string( 32-bytes ) 
	 * @param data_size   size of data in buffer ( =32 )
	 *
	 * @return hash value of data ( in [ min .. max ] )
	 *
	 *********************************************************************/
	virtual	int	map( const void* data, size_t data_size )
	{
		if ( 32 != data_size ) {
			return	-1;
		}

		char	md5bin[ 32 ] = { 0 };
		size_t	md5bin_size = sizeof(md5bin);
		hexdecode( (const char* )data, md5bin, &md5bin_size );
		if ( 16 != md5bin_size ) {
			return	-1;
		}

		unsigned val = toint( md5bin, _map_size );

		int	x = (val % _val_size) + min();
		return	x;
	}

private	:
	int	_val_size;
	int	_map_size;
};	//// class Md5hexMapper



#endif	//// __N_MAPER_HPP_2010__


